package uy.org.softtech.cssuite.helpers;

import static org.apache.commons.lang.StringUtils.containsAny;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimWhitespace;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.bind.annotation.XmlTransient;

/**
 * Clase con métodos utilitarios para trabajar con contraseñas.
 *
 * @author fabian.lobo
 */
@XmlTransient
public class HelperPassword {
	
	private static final char numeros[] = "0123456789".toCharArray();
	private static final char minusculas[] = "abcdefghijklmnopqrstuvwxyz".toCharArray();
	private static final char mayusculas[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
	private static final char especiales[] = "`~!@#$%^&()-_=[{]}\\|;:'\",<.>/?".toCharArray();
    private static final SecureRandom random = new SecureRandom(String.valueOf(HelperFechaHora.fechaActual()).getBytes());

	/**
	 * Valida si el texto dado puede ser usado como contraseña en la aplicación. Sigue las siguientes reglas:
	 *
	 * <ul>
	 * 		<li>Tener entre 8 y 20 caracteres</li>
	 * 		<li>Tener por lo menos una letra mayúscula</li>
	 * 		<li>Tener por lo menos una letra minúscula</li>
	 * 		<li>Tener por lo menos un número</li>
	 * 		<li>Tener por lo menos un caracter especial (`~!@#$%^&()-_=[{]}|;:'",<.>/?)</li>
	 * </ul>
	 *
	 * @return true, si es una password válida, false en caso contrario
	 * @author fabian.lobo
	 */
	public synchronized static Boolean validaPassword(String candidato) {
		boolean tieneLargoCorrecto = hasText(trimWhitespace(candidato)) && candidato.length() >= 8 && candidato.length() <= 20;
		boolean tieneUnaMayuscula = containsAny(candidato, mayusculas);
		boolean tieneUnaMinuscula = containsAny(candidato, minusculas);
		boolean tieneUnNumero = containsAny(candidato, numeros);
		boolean tieneUnCaracterEspecial = containsAny(candidato, especiales);
		
		return tieneLargoCorrecto && tieneUnaMayuscula && tieneUnaMinuscula && tieneUnNumero && tieneUnCaracterEspecial;
	}
	
	/**
	 * Retorna un texto aleatorio para ser usado como contraseña en la aplicación. Sigue las siguientes reglas:
	 *
	 * <ul>
	 * 		<li>Tener entre 8 y 20 caracteres</li>
	 * 		<li>Tener por lo menos una letra mayúscula</li>
	 * 		<li>Tener por lo menos una letra minúscula</li>
	 * 		<li>Tener por lo menos un número</li>
	 * 		<li>Tener por lo menos un caracter especial (`~!@#$%^&()-_=[{]}|;:'",<.>/?)</li>
	 * </ul>
	 *
	 * @author fabian.lobo
	 */
	public synchronized static String passwordAutogenerada(){
		       
        int largo = random.nextInt(12) + 8;
        
        List<Character> letras  = new ArrayList<Character>(largo);
        letras.add(numeros[random.nextInt(numeros.length)]);
        letras.add(minusculas[random.nextInt(minusculas.length)]);
        letras.add(mayusculas[random.nextInt(mayusculas.length)]);
        letras.add(especiales[random.nextInt(especiales.length)]);
        
        for(int cont = 5; cont <= largo; cont++){
            int valor = random.nextInt(3);
            switch(valor){
	            case 3:
                	letras.add(especiales[random.nextInt(especiales.length)]);
                    break;
	            case 2:
                    letras.add(mayusculas[random.nextInt(mayusculas.length)]);
                    break;
	            case 1:
                    letras.add(numeros[random.nextInt(numeros.length)]);
                    break;
	            default:
                    letras.add(minusculas[random.nextInt(minusculas.length)]);
                    break;
            }
        }

        StringBuilder builder = new StringBuilder();
        Collections.shuffle(letras);

        for (int s = 0; s < letras.size(); s++) {
        	builder.append(letras.get(s));
        }

        return builder.toString();
	}
}
